package com.soup.hummingbird.module.method.probe;

import com.soup.hummingbird.module.method.config.ProfilingFilter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * 〈一句话功能简述〉<br>
 *
 * @author zhaoyi
 * @date 2021-04-04 21:25
 * @see [相关类/方法]（可选）
 * @since [产品/模块版本] （可选）
 */
public class ProfilingClassAdapter extends ClassVisitor {
    private final String className;
    private final String fullClazzName;
    private final String simpleClassName;

    private boolean isInterface;

    public ProfilingClassAdapter(final ClassVisitor cv, String className) {
        super(Opcodes.ASM5, cv);
        this.className = className;
        this.fullClazzName = className.replace('/', '.');
        this.simpleClassName = className.substring(className.lastIndexOf('/') + 1);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);
        this.isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        // 对于接口和私有方法不进行插桩注入
        if (isInterface || (access & Opcodes.ACC_PRIVATE) != 0) {
            return super.visitMethod(access, name, descriptor, signature, exceptions);
        }
        // 不对抽象方法、native方法、桥接方法、合成方法进行插桩注入
        if ((access & Opcodes.ACC_ABSTRACT) != 0
                || (access & Opcodes.ACC_NATIVE) != 0
                || (access & Opcodes.ACC_BRIDGE) != 0
                || (access & Opcodes.ACC_SYNCHRONIZED) != 0) {
            return super.visitMethod(access, name, descriptor, signature, exceptions);
        }
        // 不对构造方法进行插桩注入
        if ("<init>".equals(name) || "<clinit>".equals(name)) {
            return super.visitMethod(access, name, descriptor, signature, exceptions);
        }
        // Object的默认方法不进行插桩注入
        if (ProfilingFilter.isNotNeedInjectMethod(name)) {
            return super.visitMethod(access, name, descriptor, signature, exceptions);
        }
        MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);
        if (null == mv) {
            return null;
        }
        return new ProfilingMethodVisitor(access, name, descriptor, mv, className, fullClazzName, simpleClassName);
    }
}
